<?php
namespace Websocket;
require_once 'Net/Connection.php';
require_once 'WebsocketConnection.php';
require_once __DIR__ .'/../Net/Event.php';

use Net\Event;
use Websocket\WebsocketConnection;

class WebsocketClient extends WebsocketConnection
{
    protected $_key=null;
    
    protected $_lastError;
    protected $_lastErrorMessage;
    
    public function getLastError()
    {
        return $this->_lastError ?: parent::getLastError();
    }
    
    public function getLastErrorMessage()
    {
        return $this->_lastErrorMessage ?: parent::getLastErrorMessage();
    }
    
    /**
     * 连接websocket服务器
     * @param $address
     * @param int $port
     * @return $this|\Websocket\WebsocketConnection
     */
    public function connect($address, $port = 0)
    {
        if($this->isBlockMode()) {
            parent::connect($address, $port);
            
            if(!$this->getLastReturn()) {
                return $this;
            }
            
            $this->_sendHandshakeRquest();
            $this->_processingHandshakeResponse($this->recv());
        } else {
            $this->onReady(function(Event $e) {
                if(!$this->handshake) {
                    $e->stopTransmit();
                    $this->_sendHandshakeRquest();
                }
            });
            
            $this->onRecv(function(Event $e) {
                $message = $e->getMessage();
                
                if($this->handshake) {
                    $buffer = $this->getBuffer();
                    $buffer->load($message);
                    
                    foreach ($buffer->fetchAllMessages() as $message) {
                        $this->triggerOnMessage([Event::PARAM_MESSAGE => $message]);
                    }
                } else {
                    $this->_processingHandshakeResponse($message);
                }
            });
            
            parent::connect($address, $port);
        }
    }
    
    /**
     * 从websocket服务器中接收一条消息, 若消息不完整则直到接收完整消息
     * @param bool $returnBuffer
     * @return bool|mixed|WebsocketBuffer
     */
    public function recvMessage($returnBuffer=false) {
        if(!$this->handshake) {
            return false;
        }
        
        $buffer = $this->getBuffer();
        
        do {
            if($this->isRecvClose()) {
                return $returnBuffer ? $buffer : $buffer->fetchMessage();
            }
            
            if($buffer->isClosed()) {
                $this->_recvclose = true;
                $this->close();
                return $returnBuffer ? $buffer : $buffer->fetchMessage();
            }
            
            $buffer->load($this->recv());
    
            if($buffer->hasMessage()) {
                return $returnBuffer ? $buffer : $buffer->fetchMessage();
            }
        } while(true);
    }
    
    protected function _sendHandshakeRquest() {
        $peername = $this->getpeername();
        $this->_key = base64_encode(md5(microtime()));
        $requestHeaders = [
            "GET ws://{$peername['address']}:{$peername['port']}/ HTTP/1.1",
            "Host: {$peername['address']}:{$peername['port']}",
            "Connection: Upgrade",
            "Upgrade: websocket",
            "Sec-WebSocket-Version: 13",
            "Sec-WebSocket-Key: {$this->_key}",
        ];
    
        $request = implode("\r\n", $requestHeaders) . "\r\n\r\n";
        $this->send($request);
    }
    
    protected function _processingHandshakeResponse($message) {
        list($header, $body) = self::parseHttpProtocol($message);
    
        $connection = strtolower($header['connection']);
        $upgrade = strtolower($header['upgrade']);
        
        if($connection != 'upgrade' || $upgrade != 'websocket') {
            $this->_lastError = 'handshake_protocol_error';
            $this->_lastErrorMessage = '服务器响应的协议错误, 无法完成握手';
            
            $this->triggerOnConnectFail([
                Event::PARAM_ERROR_CODE => $this->_lastError,
                Event::PARAM_ERROR_MESSAGE => $this->_lastErrorMessage
            ]);
            
            $this->close();
            return;
        }
    
        if($header['sec-websocket-accept'] != $this->encodeKey($this->_key)) {
            $this->_lastError = 'handshake_key_error';
            $this->_lastErrorMessage = '服务器响应的sec-websocket-accept与之前的不匹配, 无法完成握手';
            
            $this->triggerOnConnectFail([
                Event::PARAM_ERROR_CODE => $this->_lastError,
                Event::PARAM_ERROR_MESSAGE => $this->_lastErrorMessage
            ]);
            
            $this->close();
            return;
        }
        
        $this->handshake = true;
        $this->triggerOnReady();
    }
}
